#ifndef _TEXTURE_H_
#define _TEXTURE_H_


#include <stdint.h>
#include <functional>
#include <memory>
#include <SERender/Drive/Drive.h>
#include <glm/glm.hpp>

namespace SEngine
{
    template<typename T>
    class TextureLoader;

    class Texture
    {
        template<typename T>
        friend class TextureLoader;
    public:
        Texture();
        virtual ~Texture();

        virtual void Bind(int slot = 0) = 0;
        virtual void Unbind() = 0;
        uint32_t GetId() { return m_id; }
        ColorFormat GetFormat() { return m_fmt; }

    private:
        void SetFormat(ColorFormat fmt) { m_fmt = fmt; }
    protected:
        ColorFormat m_fmt;
        uint32_t m_id;
        int m_slot;
    };

    class Texture2D : public Texture
    {
        template<typename T>
        friend class TextureLoader;
    public:
        Texture2D() : Texture() {}
        virtual ~Texture2D() = default;
        void Bind(int slot = 0) override;
        void Unbind() override;
        void Resize(int w, int h) { 
            glm::ivec2 newSize{w, h};
            if (m_lastSize != newSize) {
                Bind(); 
                m_resize(w, h); 
            }
            m_lastSize = newSize;
        }
        static std::shared_ptr<Texture2D> GetDefaultTexture();
    private:
        std::function<void(int, int)> m_resize;
        glm::ivec2 m_lastSize{0};
    };

    class TextureHDR : public Texture2D
    {
    public:
        TextureHDR() : Texture2D() {}
        virtual ~TextureHDR() = default;
    };

    class TextureCube : public Texture2D
    {
    public:
        TextureCube() : Texture2D() {}
        virtual ~TextureCube() = default;
        void Bind(int slot = 0) override;
        void Unbind() override;
        static std::shared_ptr<TextureCube> GetDefaultTexture();
    };

    class Texture2DMultiSample : public Texture
    {
    public:
        Texture2DMultiSample() : Texture() {}
        virtual ~Texture2DMultiSample() = default;
        void Bind(int slot = 0) override;
        void Unbind() override;
    };

    class Texture3D : public Texture
    {
        template<typename T>
        friend class TextureLoader;
    public:
        Texture3D() : Texture() {}
        virtual ~Texture3D() = default;
        void Bind(int slot = 0) override;
        void Unbind() override;
        void Resize(int w, int h, int d) { 
            glm::ivec3 newSize(w, h, d);
            if (m_lastSize != newSize) {
                Bind(); 
                m_resize(w, h, d); 
            }
            m_lastSize = newSize;
        }
    private:
        std::function<void(int, int, int)> m_resize;
        glm::ivec3 m_lastSize{0};
    };

} // namespace SEngine


#endif